msg_tool\scripts\yaneurao\itufuru/
script.rs1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::{decode_to_string, encode_string};
6use anyhow::Result;
7
8#[derive(Debug)]
9pub struct ItufuruScriptBuilder {}
11
12impl ItufuruScriptBuilder {
13 pub const fn new() -> Self {
15 ItufuruScriptBuilder {}
16 }
17}
18
19impl ScriptBuilder for ItufuruScriptBuilder {
20 fn default_encoding(&self) -> Encoding {
21 Encoding::Cp932
22 }
23
24 fn build_script(
25 &self,
26 data: Vec<u8>,
27 _filename: &str,
28 encoding: Encoding,
29 _archive_encoding: Encoding,
30 config: &ExtraConfig,
31 _archive: Option<&Box<dyn Script>>,
32 ) -> Result<Box<dyn Script>> {
33 Ok(Box::new(ItufuruScript::new(data, encoding, config)?))
34 }
35
36 fn extensions(&self) -> &'static [&'static str] {
37 &[]
38 }
39
40 fn script_type(&self) -> &'static ScriptType {
41 &ScriptType::YaneuraoItufuru
42 }
43}
44
45#[derive(Debug)]
46struct ItufuruString {
47 instr: u16,
48 len_pos: usize,
49 len: u16,
50}
51
52#[derive(Debug)]
53pub struct ItufuruScript {
55 data: MemReader,
56 strings: Vec<ItufuruString>,
57 encoding: Encoding,
58}
59
60impl ItufuruScript {
61 pub fn new(buf: Vec<u8>, encoding: Encoding, _config: &ExtraConfig) -> Result<Self> {
67 let mut reader = MemReader::new(buf);
68 let mut strings = Vec::new();
69 let len = reader.data.len();
70
71 while reader.pos + 1 < len {
72 let instr = reader.read_u16()?;
73 if instr == 0x2 || instr == 0x1e || instr == 0x1 || instr == 0x13 || instr == 0x27 {
79 let len_pos = reader.pos;
80 let len = reader.read_u16()?;
81 match reader.read_cstring() {
82 Ok(s) => {
83 let slen = s.as_bytes_with_nul().len() as u16;
84 if slen != len {
85 reader.pos = len_pos;
86 continue;
87 }
88 if instr == 0x2 && !s.as_bytes().ends_with(b"\n") {
89 reader.pos = len_pos;
90 continue;
91 }
92 if len < 3 {
93 reader.pos = len_pos;
94 continue;
95 }
96 if instr != 0x2 && instr != 0x1e {
97 continue;
98 }
99 strings.push(ItufuruString {
100 instr,
101 len_pos,
102 len,
103 });
104 }
105 Err(_) => {
106 reader.pos = len_pos;
107 continue;
108 }
109 }
110 }
111 }
112
113 Ok(ItufuruScript {
114 data: reader,
115 strings,
116 encoding,
117 })
118 }
119}
120
121impl Script for ItufuruScript {
122 fn default_output_script_type(&self) -> OutputScriptType {
123 OutputScriptType::Json
124 }
125
126 fn default_format_type(&self) -> FormatOptions {
127 FormatOptions::None
128 }
129
130 fn extract_messages(&self) -> Result<Vec<Message>> {
131 let mut messages = Vec::new();
132 for i in self.strings.iter() {
133 let str_pos = i.len_pos + 2; let s = self.data.cpeek_cstring_at(str_pos as u64)?;
135 let decoded = decode_to_string(self.encoding, s.as_bytes(), true)?;
136 messages.push(Message {
137 name: None,
138 message: decoded,
139 });
140 }
141 Ok(messages)
142 }
143
144 fn import_messages<'a>(
145 &'a self,
146 messages: Vec<Message>,
147 mut file: Box<dyn WriteSeek + 'a>,
148 _filename: &str,
149 encoding: Encoding,
150 replacement: Option<&'a ReplacementTable>,
151 ) -> Result<()> {
152 if self.strings.len() != messages.len() {
153 return Err(anyhow::anyhow!(
154 "Number of messages does not match the number of strings in the script"
155 ));
156 }
157 let mut old_pos = 0;
158 for (old, new) in self.strings.iter().zip(messages) {
159 if old_pos < old.len_pos {
160 file.write_all(&self.data.data[old_pos..old.len_pos])?;
161 old_pos = old.len_pos;
162 }
163 let mut nstr = new.message;
164 if let Some(repl) = replacement {
165 for (from, to) in repl.map.iter() {
166 nstr = nstr.replace(from, to);
167 }
168 }
169 if old.instr == 0x2 && !nstr.ends_with('\n') {
170 nstr.push('\n');
171 }
172 let encoded = encode_string(encoding, &nstr, false)?;
173 let new_len = encoded.len() as u16 + 1;
174 file.write_u16(new_len)?;
175 file.write_all(&encoded)?;
176 file.write_all(&[0])?; old_pos += 2 + old.len as usize;
178 }
179 if old_pos < self.data.data.len() {
180 file.write_all(&self.data.data[old_pos..])?;
181 }
182 Ok(())
183 }
184}